//const mg2 = require("../old/mg2"); // should be referred tro as rich-trailmarked-text editor (function () { var path async function initLocalforage() { if (!window.lfDOTS) { window.lf = parent.localforage window.lfDOTS = window.lf.createInstance({ name: 'DOTS', storeName: 'dots' }); } return window.lfDOTS } // initLocalforage() function formatDoc(oDoc, sCmd, sValue) { if (!validateMode(oDoc)) { return; } document.execCommand(sCmd, false, sValue); //- ftp : automatically set foreground color white for blue green background if (sCmd === 'backcolor' && (sValue === 'red' || sValue === 'green' || sValue === 'blue')) { console.log('change foregraound to white') document.execCommand("forecolor", false, "white"); } oDoc.focus(); } function validateMode(oDoc) { if (!document.getElementById("rte-mode-" + rId.exec(oDoc.id)[0]).checked) { return true; } alert("Uncheck \u00AB" + sModeLabel + "\u00BB."); oDoc.focus(); return false; } function extractText(oDoc) { if (oDoc.innerText) { return oDoc.innerText; } var oContent = document.createRange(); oContent.selectNodeContents(oDoc.firstChild); return oContent.toString(); } function setDocMode(oDoc, bToSource) { window.oDoc = oDoc if (bToSource) { var oContent = document.createTextNode(oDoc.innerHTML), oPre = document.createElement("pre"); oDoc.innerHTML = ""; oDoc.contentEditable = false; oPre.className = "rte-sourcetext"; oPre.id = "rte-source-" + oDoc.id; oPre.onblur = oDoc.onblur; oPre.contentEditable = true; oPre.appendChild(oContent); oDoc.appendChild(oPre); } else { oDoc.innerHTML = extractText(oDoc); oDoc.contentEditable = true; } oDoc.focus(); } function menuSelect() { if (this.selectedIndex < 1) { return; } if (this.id.startsWith("extra")) { sCmd = this.id.slice(0, -1) let option = this[this.selectedIndex] let sAction = option.value handleExtra(sCmd, sAction) this.selectedIndex = 0; return 0 } var sMenuGroup = rId.exec(this.id)[0], sCmd = this.id.slice(0, -sMenuGroup.length); formatDoc(aEditors[sMenuGroup], sCmd, this[this.selectedIndex].value); this.selectedIndex = 0; } function handleExtra(sCmd, sAction) { let selection = getSelection() let parentNode = selection.rangeCount > 0 ? selection.getRangeAt(0).commonAncestorContainer.parentNode : null let handler = { "expand": function (node) { $(node).siblings().toArray().splice(0).map(function (i) { $(i).show() }) }, "collapse": function (node) { $(node).siblings().toArray().splice(0).map(function (i) { $(i).hide() }) }, "anchor": function (node) { //- issue : add anchor for clue //- fix : remove spaces from anchorName // show anchorName in title let anchorName = node.innerText.substring(0, 24).replace(/ /g, "") let anchor = '$ ' document.execCommand('insertHtml', false, anchor) console.info("inserted anchor " + node.innerText.substring(24)) }, } let munger = { "collapseAll": function () { $('ul li').hide() // x = $('ul').toArray() // x.map(function (z) { $('li', z).first().show() }) }, "expandAll": function () { $('li').show() $('ul').show() // $('ul li').hide() x = $('ul').toArray() x.map(function (z) { $('li').hide() $('li', z).first().show() }) }, "clean": function () { opidox.rte.cleanElem('div[style]') }, //- feat : opidox.rte.interpretPropOnPage " - i ": function () { let choice = confirm("Would ypu like to execute property marks on the") if (choice) { opidox.rte.interpretPropOnPage('i') } } } munger[sAction]() if (parentNode) { (handler[sAction])(parentNode) } } function buttonClick() { var sBtnGroup = rId.exec(this.id)[0], sCmd = this.id.slice(0, -sBtnGroup.length); var cc = 0 if (sCmd === "count") { let selection = window.getSelection().toString() console.log(selection) if (selection) { cc = selection.length spacesRemoved = selection.replace(/ /g, '').length wordCount = cc - spacesRemoved countTodisplay = cc + " w" + wordCount $('.ccSpan').text(countTodisplay) document.execCommand("copy"); window.getSelection().empty() } else { $('.ccSpan').text($('.rte-editbox').text().length) } return } customCommands.hasOwnProperty(sCmd) ? customCommands[sCmd](aEditors[sBtnGroup]) : formatDoc(aEditors[sBtnGroup], sCmd, this.alt || false); } function changeMode() { setDocMode(aEditors[rId.exec(this.id)[0]], this.checked); } function updateField() { var sFieldNum = rId.exec(this.id)[0]; document.getElementById("rte-field-" + sFieldNum).value = document.getElementById("rte-mode-" + sFieldNum).checked ? extractText(this) : this.innerHTML; } function createMenuItem(sValue, sLabel) { var oNewOpt = document.createElement("option"); oNewOpt.value = sValue; oNewOpt.innerHTML = sLabel || sValue; return oNewOpt; } function createEditor(oTxtArea) { var nEditorId = aEditors.length, oParent = document.createElement("div"), oMenuBar = document.createElement("div"), oToolsBar = document.createElement("div"), oEditBox = document.createElement("div"), oModeBox = document.createElement("div"), oModeChB = document.createElement("input"), oModeLbl = document.createElement("label"); oParent.className = "rich-text-editor"; oParent.id = oTxtArea.id || "rich-text-" + nEditorId; oMenuBar.className = "rte-menus"; oToolsBar.className = "rte-tools"; oEditBox.className = "rte-editbox"; oEditBox.id = "rte-editbox-" + nEditorId; oEditBox.contentEditable = true; oEditBox.innerHTML = oTxtArea.value; aEditors.push(oEditBox); if (oTxtArea.form) { var oHiddField = document.createElement("input"); oHiddField.type = "hidden"; oHiddField.name = oTxtArea.name; oHiddField.value = oEditBox.innerHTML; oHiddField.id = "rte-field-" + nEditorId; oTxtArea.form.appendChild(oHiddField); oEditBox.onblur = updateField; } for (var oMenu, oMenuOpts, vOpt, nMenu = 0; nMenu < oTools.menus.length; nMenu++) { oMenu = document.createElement("select"); oMenu.id = oTools.menus[nMenu].command + nEditorId; oMenu.onchange = menuSelect; oMenu.appendChild(createMenuItem(oTools.menus[nMenu].header)); oMenuOpts = oTools.menus[nMenu].values; if (oMenuOpts.constructor === Array) { for (vOpt = 0; vOpt < oMenuOpts.length; oMenu.appendChild(createMenuItem(oMenuOpts[vOpt++]))); } else { for (vOpt in oMenuOpts) { oMenu.appendChild(createMenuItem(vOpt, oMenuOpts[vOpt])); } } oMenu.selectedIndex = 0; oMenuBar.appendChild(document.createTextNode(" ")); oMenuBar.appendChild(oMenu); } for (var oBtnDef, oButton, nBtn = 0; nBtn < oTools.buttons.length; nBtn++) { oBtnDef = oTools.buttons[nBtn]; oButton = document.createElement("img"); oButton.className = "rte-button"; oButton.id = oBtnDef.command + nEditorId; oButton.src = oBtnDef.image; if (oBtnDef.hasOwnProperty("value")) { oButton.alt = oBtnDef.value; } oButton.title = oBtnDef.text; oButton.onclick = buttonClick; oToolsBar.appendChild(oButton); } // add a span to hold count var countSpan = document.createElement("b"); countSpan.className = "ccSpan"; oToolsBar.appendChild(countSpan) oModeBox.className = "rte-switchmode"; oModeBox.title = sModeLabel; oModeChB.type = "checkbox"; oModeChB.id = "rte-mode-" + nEditorId; oModeChB.onchange = changeMode; // oModeLbl.setAttribute("for", oModeChB.id); // oModeLbl.innerHTML = sModeLabel; oModeBox.appendChild(oModeChB); oModeBox.appendChild(document.createTextNode(" ")); oModeBox.appendChild(oModeLbl); oParent.appendChild(oMenuBar); oParent.appendChild(oToolsBar); oParent.appendChild(oEditBox); oMenuBar.appendChild(oModeBox); oTxtArea.parentNode.replaceChild(oParent, oTxtArea); // gyuri var editArea = $('#' + oTxtArea.id); editArea.addClass("scrolling") editArea.bind('DOMNodeInserted', function (event) { if (event.originalEvent && event.originalEvent.target) { var target = $(event.originalEvent.target); //now you can check what has been moved } }); $('.rich-text-editor').css({ "width": (window.innerWidth - 40) + 'px' }) $('.rte-editbox').css({ "width": (window.innerWidth - 50) + 'px' }) } function replaceFields(nFlag) { nReady |= nFlag; if (nReady !== 3) { return; } for ( var oField, nItem = 0, aTextareas = Array.prototype.slice.call(document.getElementsByTagName("textarea"), 0); nItem < aTextareas.length; oField = aTextareas[nItem++], oField.className !== "rich-text-editor" || createEditor(oField) ); ; opidox.rte.setup() } function toolsReady() { oTools = JSON.parse(this.responseText); replaceFields(2); } function documentReady() { replaceFields(1) } var oTools, nReady = 0, sModeLabel = "Show HTML", aEditors = [], rId = /\d+$/, oToolsReq = new XMLHttpRequest(), customCommands = { "printDoc": function (oDoc) { if (!validateMode(oDoc)) { return; } var oPrntWin = window.open("", "_blank", "width=450,height=470,left=400,top=100,menubar=yes,toolbar=no,location=no,scrollbars=yes"); oPrntWin.document.open(); oPrntWin.document.write("Print<\/title><\/head><body onload=\"print();\">" + oDoc.innerHTML + "<\/body><\/html>"); oPrntWin.document.close(); }, "cleanDoc": function (oDoc) { if (validateMode(oDoc) && confirm("Are you sure?")) { oDoc.innerHTML = ""; } ; }, "createLink": function (oDoc) { let selection = window.getSelection().toString() var sLnk = prompt("Write Dot Title/URL ", selection); let target = sLnk if (sLnk) { //- fn : ensure link target does not have leading/trailing white space let target = sLnk.trim() if (!sLnk.startsWith("http")) { let newDotLink = "/wiki?t=" + selection if (newDotLink) { target = newDotLink //- ftp : dead wood opidox.rte.action.createDot(selection, sLnk) } } formatDoc(oDoc, "createlink", target); } }, "save": function () { opidox.rte.save() //- ftp : RTE setup on save and indicate saved status opidox.rte.setup() $('#isSaved').attr('checked', true) //window.minddrive.save2MindDrive(dot) // opener.hostapp.run('/minddrive/save', n) } }; oToolsReq.onload = toolsReady; oToolsReq.open("GET", "rich-text-tools.json", true); oToolsReq.send(null); window.addEventListener ? addEventListener("load", documentReady, false) : window.attachEvent ? attachEvent("onload", documentReady) : window.onload = documentReady; })(); window.opidox = window.opidox || {} opidox.rte = (function () { let _rte = {} let n; //- fix : When running in trailNext there is no parent iframe let oldTrailMarks = true //- fix : access to mg2 in editor try { if (parent.mg2) { parent.parent.dot = parent.mg2.G2 oldTrailMarks = true mg3 = mg2 || parent.mg2 } } catch (e) { } function hideSubLists() { $('ul>ul').hide() } //- ftp : toggle top level clues function toggle2(ev) { console.log("toggle2", ev.offsetX) //- issue : Prevent top level clue collapsing if (ev.offsetX > 13 && ev.ctrlKey) { // console.log("here") listItemAction(ev, ev.target) return } if (ev.offsetX < 13) { let next = $(ev.target).next() //console.log(next.show()) let firstChild = next if ($(ev.target).is('span')) { firstChild = $(ev.target.parentElement).next() } if (next.is('div')) { firstChild = next.children().first() } if (firstChild && firstChild.is('ul')) { if (firstChild && firstChild.is(':visible')) { firstChild.hide() } else { firstChild.show() } return false } } else { return false } } //- obsolated : toggle - by : - ftp : toggle top level clues function toggle(ev) { // hideSubLists() return console.log("one", ev.offsetX) let next = $(ev.target).next() //console.log(ev.offsetX) if (ev.offsetX > 13 && ev.ctrlKey) { // console.log("here") listItemAction(ev, ev.target) return } else { return } // Stop expanding next item if it is a list if (ev.target.tagName === 'LI' && $(ev.target).next()[0].tagName === 'LI') { return } let listItem = ev.target.tagName === 'LI' ? $(ev.target) : $(ev.target).parent() if (listItem.next()[0].style.display === "none") { listItem.next().show() } else { listItem.next().hide() } $('ul', listItem.next()).show() // show character and wordcount when toggling blog let selection = listItem.children(); //- fix : check for non empty array if (selection && selection.length > 0) { selection.toArray().reduce(function (a, b) { return a + $.trim($(b).text()) }) cc = selection.length console.log(selection) spacesRemoved = selection.replace(/ /g, '').length wordCount = cc - spacesRemoved countTodisplay = cc + " w" + wordCount $('.ccSpan').text(countTodisplay) } } function listItemAction(ev, li) { //console.log($(li).html()) // before toggline // check for shift key if (ev.ctrlKey) { let x = $(ev.target).text() if (x) { let action = x.split(':')[0].split('-')[1].trim() function handleIntent(action) { let intents = {} let intent = intents[action] if (!intent) { function showContext(target) { let jqe = $(target) console.log(jqe.html()) } showContext($(ev.target).parent()) } } handleIntent(action) alert(action) } return true } } function clickerForTop() { //- ftp : toggle top level clues $('div', $('#rte-editbox-0')).click(toggle2) $('li').click(toggle2) $('li').toArray().forEach(function (e) { let highlightColor = localStorage.HIGHLIGHT_COLOR if (!localStorage.HIGHLIGHT_COLOR) { localStorage.HIGHLIGHT_COLOR = '#DDDDFF' } if (e.nextElementSibling && e.nextElementSibling.tagName === "UL") { $(e).css("background-color", localStorage.HIGHLIGHT_COLOR) } /* if (e.parentElement.firstElementChild.tagName==="LI") { $(e.parentElement.firstElementChild).css("background-color","#FAFAFF") } console.log(e) */ }) /* $('li').toArray().forEach(function (e) { console.log(e) }) */ } function setupAnnotationSearch() { // trigger memex search var p = $('ul.wbs li'); $('.annotationTrigger', p).remove() p.each(function (x, e) { let text = e.innerText text = text.trim() text = text.replace(/[\n\r]+|[\s]{2,}/g, '+') text = text.replace(/ /g, '+') let targetLocation = 'chrome-extension://abkfbakhjpmblaafnpgjppbmioombali/options.html#/overview?query=' + text let open = "window.open('" + targetLocation + "','_self')" // open = 'location.href=' let link2memexSearch = $('<span class="annotationTrigger">' + '<a title="Right click to open in new window, cap Notes tab to narrow" target="_self" href="' + targetLocation + '" > <img width="32" src="https://adminpick.com/wp-content/uploads/logos/memex.png"></a>' + '</span>') e.append(link2memexSearch[0]) }) /* p.attr('unselectable', 'on') .css('user-select', 'none') .on('selectstart', false); p.dblclick(function (e) { let li = $(e.target) let text =li.text() text = text.replace(/[\n\r]+|[\s]{2,}/g, '+') text ="Chromium" let memexSearch = "chrome-extension://abkfbakhjpmblaafnpgjppbmioombali/options.html#/overview?query=" + text openInNewTab(memexSearch) $(e).attr('unselectable', 'on') .css('user-select', 'none') .on('selectstart', false); p.css("cursor", "text") .attr('contenteditable', true) .attr('unselectable', 'off') .css('user-select', 'inherit') .off('selectstart', false) .focus(); }); // setup a click handler // $('li').dblclick(function (ev) { // console.log(ev) // }) */ } //- feat : convert data0images in slides _rte.setup = async function (dot) { let hubOrigin = 'http://localhost:8080' function sendRequestViaOpenChannel(cont) { let indieHubFrame = document.getElementById("IndieHubFrame") if (false && !indieHubFrame.src) { indieHubFrame.src = host + "/hub" + location.search + "&ts=" + moment().unix() indieHubFrame.contentWindow.addEventListener('load', function () { cont(hub.scld.x.getDrone()) }, false) } } // retrofit if (screen.availHeight < 801) { $('.rte-editbox').css("max-height", "600").css("width", "480px") //alert("x2e48") } /* if (!dot) { if (oldTrailMarks) { //- ftp : access mg2 from parent n = parent.mg2.G2.getFocus().n() } else { n = mg2.dot.getFocus() } } */ let ps = new URLSearchParams(location.search.substring(1)) let t = ps.get('t') if (!t) { t = location.search.substring(1) } /* else { path = ps.getAll('path') if (path.lengt>0) { path = path[0] } else {path = ""} } */ if (t) { let td = decodeURIComponent(t) //- bug : sending back received dot let title = decodeURIComponent(t) let dots = mindgraph.dot.byTitle(title, true) //- ftp : no dot matching title found if (dots.length === 0) { //TODO get is scribe // create dot that does not exists here let dot = { t: decodeURIComponent(title), s: "stub - " + title, i: "https://bafybeihvcrddb4zbq5t4zuj7q4esabf5mizoxvkjvoac7gmmf73fcnqou4.ipfs.w3s.link/dot.png" } dots = [mindgraph.dot.save(dot)] return // - fn : sendRequestViaOpenChannel // sendRequestViaOpenChannel(function (drone) { const rq = 'getByTitle' const from = localStorage.peerName const to = 'scribe' const title = t const m = { rq, from, to, title } // drone(JSON.stringify(m)) // window.hub.scld.x.queueMessage(JSON.stringify(m)) debugger //TODO : clarify host if (true || location.host === "indyhub1.fission.app") { hubOrigin = location.origin hubOrigin = location.origin + location.pathname } debugger open(hubOrigin + './hub/chat/?' + encodeURIComponent(JSON.stringify(m))) // hub.scld.x.add(message) // }) return } if (dots.length === 1) { n = dots[0] } if (dots.length > 0) { //- fix : handling unique dots let dots2 = mindgraph.dot.byTitle(decodeURIComponent(t), true) if (dots2.length === 1) n = dots2[0] } //- ftp : alert if match by title is not unique else { alert("title is not unique") console.log(dots) } } if (!n) { // location.href = '/demo/#dot/search/' + t return } // - ftp : Make stub editable in RTE let dotSize = encodeURIComponent(JSON.stringify(n)).length let stubEdit = '<br><textarea cols="60" id="stub" type="text"></textarea>' + '<button id="saveBtn">SAVE</button>' + '<input id="shareCheckBox" title="share in hub to feed on save" ' + 'type="checkbox"></input> share' + '<input title="with provenance on save" ' + 'type="checkbox" id="provenance"></input> with provenance' + '<a target="_blank" href="/reveal/?' + n.t + '">slides</a> ' + '<a target="_blank" href="/wcg/?t=' + n.t + '">WCG</a>' + '<b title="size of dot string" id="dotSize">' + dotSize + '</b>' //- issue : - add : self link to dot shown in editor //- ftp : display time created //- TODO : ensure ct exist in MindGraph if (!n.le) { if (n.lu) { n.le = n.lu } } //TODO del with turning off //REMEMBER to turn off shared status of node when replicated in push let timeLastEdited = moment(parseInt(n.le) * 1000).format("YYYY MMM DD hh:mm") let timeCreated = "" if (n.ct) { timeCreated = moment(parseInt(n.ct) * 1000).format("YYYY MMM DD") } //- issue : show if saved and prevent reload if edited but not saved //- fix : links for module with ../ and ? let selfLink = '<a target="_blank" href="../wiki/?t=' + n.t + '">' + timeLastEdited + '</a><input type="checkbox" id="isSaved">' let dragLink = '<span style="float:right"><a target="_blank" href="../wiki/?t=' + n.t + '">' + n.t + '</a></span>' let hypermap = '<a target="_blank" href="/hypermap?a=' + n.a + '&p=' + localStorage.user + '">' + 'hypermap' + '</a>' let shared2 = '<textarea id="sharedToList" rows="2" cols="50"></textarea>' let headerShow = '<div><img src="' + n.i + '" height="48px">' + shared2 + '<div style="float:right"><b style="font-size:1.5em"> ' + n.t + '</b> ' + '<a target="_blank" href="../#"><img width="48" src="http://w3s.link/ipfs/bafkreia2y7hbab46vjk7sgdcdzxann2moyeeflothfejwgtbjkthfkfw7q?filename=icon%2520-%2520IndyLab.png" ></img>Home</a> <br>' + hypermap + n.a + '  ' + timeCreated + ' ' + selfLink + '</div>' + '</div>' + stubEdit + dragLink // '<div>' + stubEdit + '<br>' + n.a + '</div>' $('#dotName').html(headerShow) //- ftp : edit icon shown on tab // for //- feat : Present Page Intent in Tab let spaceIndex = n.s.indexOf(' ') if (spaceIndex < 0) { spaceIndex = 2 } //- fix : pagemrk icon showing const stubInitial = n.s.substring(2, spaceIndex) const stubMark = n.s.substring(0, 2) let title = n.t const index = n.t.indexOf(' - ') let pageMark if (index > 0) { title = n.t.substring(index + 3) pageMark = stubMark + stubInitial + ' ' + title } else { pageMark = stubInitial + stubMark + title } $('title').text(pageMark) $('#stub').val(n.s) //- ftp : show dot.pa.shared2 /** show emojis for conversants with whom the page had been shared with */ let sharedToList = '' if (n.pa && n.pa.shared2) { sharedToList = n.pa.shared2.reduce(function (a, b) { return a + b.substring(0, 2) + ' ' }, '') } $('#sharedToList').html(sharedToList) //- ftp : show icon for dot topic let iconSrc = n.i //- ftp : use dot image or first image on page as icon for tab //- fix : ensure enclosing div for jquery img search try { let imgElements = $('img', '<div>' + n.b + '</div>') if (imgElements && imgElements.length > 0) { iconSrc = imgElements[0].src } } catch (e) { console.error(e) } //- web - how : set favicon dynamically $('link[rel="icon"]').attr('href', iconSrc) console.log(n.b) function bgetBodyLinkHref(bodyLink) { const bodyLinkPrefix = '<a target="_blank" href="' const endBodyLinkMatch = '">body<' const endBodyLinkPosition = bodyLink.indexOf(endBodyLinkMatch) const bodyLinkHref = bodyLink.substring(bodyLinkPrefix.length, endBodyLinkPosition) return bodyLinkHref } if (n.b && n.b.startsWith('<a target="_blank" href="')) { let dwebLink = bgetBodyLinkHref(n.b) // let brs = await fetch(dwebLink) /// let x = brs.text() const dotBody = await (await fetch(dwebLink)).text() // d.b = dotBody // array of bodyLinks of bodies saved on web3Storage // in reverse chronological order // hub.odb.setupOwnHub('',getBodyByHash) // async function getBodyByHash() { // let feedItem = await hub.channel.byHash(hash) // console.log(feedItem) // } n.b = dotBody } //- issue : Handle Transclusion from Archive let bodyProps = await handleTransclusion(n) async function handleTransclusion(n) { let a = n.a let b = n.b let dotBody = b if (dotBody && dotBody.startsWith('- clude : ')) { a = dotBody.substring(9).trim() if (window.hostapp && window.hostapp.lfDOTS) { let d = await window.hostapp.lfDOTS.getItem(a.trim()) if (d) { b = d.b console.log(d) } } else { b = await getFromHive(a) } } return { a: a, b: b } } //- ftp : indent on tab, outdent on shift tab $('.rte-editbox').on("keydown", function (ev) { if (ev.key === 'Tab') { if (ev.shiftKey) { $('#outdent0').click() } else { $('#indent0').click() } } //- ftp : indent on ctrl-enter if ((ev.keyCode == 10 || ev.keyCode == 13) && ev.ctrlKey) { document.execCommand('insertText', false, '\n') $('#indent0').click() } }) // Add class large automatically in editor let b2 = $('<div>' + bodyProps.b + '</div>') $('img', b2).each(function (i, x) { if (x.width > innerWidth) { $(x).addClass("large"). // fitting large images into editor css({ "max-width": "80%", "display": "block", "margin": "auto" }) } } ) // let html = formatFactory(b2.html()) let html = b2.html() if (!html.trim().startsWith('<')) { html = '<div>' + html + '</div>' } html = formatFactory(html) $("#rte-editbox-0").html(html) // fitting large images into editor $('img.large').css({ "max-width": "80%", "display": "block", "margin": "auto" }) /* document.onmouseup = document.onkeyup = document.onselectionchange = function () { let text = opidox.rte.getSelectionText(); console.log(text) } */ // hideSubLists() clickerForTop() setupAnnotationSearch() function setupPasteEventHandler() { $("#rte-editbox-0").on('paste', async function (ev) { window.x = await navigator.clipboard.readText() }) } //- ftp : reflect shared status if (n.shared) { $('#shareCheckBox').prop('checked', true) } /** * - issue : add linkClicked even hadler */ setupLinkClickeHandlers() function setupLinkClickeHandlers() { $('a').on('click', linkClicked) function linkClicked(ev) { let target = ev.target //- ftp : retorfit wiki links // using drag and drop links to dot into page creates link with full url // TODO : fix it in the editor on page load //- fix : only mungle local debugger /** jnk let path = target.href path = path.replace(/wiki\?/, "./rte/?") debugger if (target.origin === location.host) { const origin = target.origin path = target.href.substring(origin.length) } */ let path = location.origin + location.pathname + target.search let w = window.open(path, '_blank'); // w.hostapp = hostapp } } // limit max-width of images // allow copy paste of code snippets $('.nowrap').css("white-space", "nowrap") window.trailmarker.setup() $('#rte-editbox-1').focus() // - ftp : scroll to anchor if (location.hash) { let anchorText = location.hash.substring(1) let anchorEl = $('a[name="' + anchorText + '"]') //- ftp : check for anchor element position if (anchorEl.position()) { //https://hyp.is/4EB56Cq5Eey7-P98vI4YoQ/stackoverflow.com/questions/1605698/text-blinking-jquery let blinkTime = 500 let top = anchorEl.position().top - 300 console.log(top) //- ftp : slide to anchor element and blink $('.rte-editbox').animate({ scrollTop: top }, 1000); anchorEl.animate(4000, "linear", function () { let target = this.parentElement $(target).animate({ opacity: 0 }, blinkTime); $(target).animate({ opacity: 1 }, blinkTime); $(target).animate({ opacity: 0 }, blinkTime); $(target).animate({ opacity: 1 }, blinkTime); $(target).animate({ opacity: 0 }, blinkTime); $(target).animate({ opacity: 1 }, blinkTime); }); history.replaceState(null, null, '/wiki' + location.search + location.hash) } } // let webStorageIframe = document.getElementById("Web3Frame") // webStorageIframe.src = '/web3/' + '?' + moment().unix() // $('#Web3Frame').show() //- ux : add SAVE button $('#saveBtn').click(function () { _rte.save() _rte.setup() $('#isSaved').attr('checked', true) }) //- ux : toggle share status //- ftp : reflect shared status $('#shareCheckBox').click(function () { if ($('shareCheckBtn').is(':checked')) { } else { delete n.shared } }) if (navigator.platform==="Win32") { //alert("x") $('#rte-editbox-0').width(720) } } //- fn : munge image src attribute to point to git repo window.foam2dotBody = function () { let githubRepo = 'https://raw.githubusercontent.com/gyuri-lajos/gyuri/master/attachments/' $('#rte-editbox-0 img').each(function () { x = this.src.split('/'); this.src = githubRepo + x[x.length - 1] }) } _rte.clickerForTop = clickerForTop _rte.getNodeLoaded = function () { let dot = mindgraph.dot.byId(n.a) dot.b = dot.b.replace(/\n[ ]+/g, '\n ') return dot } _rte.getNodeAsEdited = function () { let nEdited = n if (!n) { return n } // make all lists visible $('ul').show() // remove annotation links $(".annotation").remove() //- issue : on Save in RTE clear highlighting of leaf list items // remove all styles $('li[style]').toArray().forEach(function (x) { $(x).removeAttr("style") }) nEdited.b = $("#rte-editbox-0").html(); nEdited.b = nEdited.b.replace(/\n[ ]+/g, '\n ') //-ftp : Make stub editable in RTE let stubText = $('#stub').val() nEdited.s = stubText return nEdited } _rte.getChangesForNode = function () { let nEdited = this.getNodeAsEdited()//.replace(/\n[ ]+/g,'\n ')() if (!nEdited) { return '' } console.log(nEdited) const nOriginal = this.getNodeLoaded()//.replace(/\n[ ]+/g,'\n ') console.log(nOriginal) let changes = { o2n: this.getDiffsBetweenNodes(nOriginal, nEdited).trim(), n2o: this.getDiffsBetweenNodes(nEdited, nOriginal).trim() } return Object.values(changes).reduce(function (a, b) { return a + b }, '') } // - for : key insight - TrailMarks // fn : rte.getDiffsBetweenNodes _rte.getDiffsBetweenNodes = function (n1, n2) { // - annote : https://hypothes.is/a/xNSn9IcxEeyJuScG-WqWiQ function diffStrings(x, y) { let diff = [...x].filter((v, idx) => [...y][idx] !== v); return diff.reduce(function (a, b) { return a + b }, "") } function diffProps(a, b, props) { let arrays = props.map(function (prop) { return diffStrings(a[prop], b[prop]) }) return arrays.reduce(function (a, b) { return a + b }) } return diffProps(n1, n2, ["t", "s", "b"]) } _rte.save = function () { // make all lists visible $('ul').show() // remove annotation links $(".annotation").remove() //- issue : on Save in RTE clear highlighting of leaf list items // remove all styles $('li[style]').toArray().forEach(function (x) { $(x).removeAttr("style") }) n.b = $("#rte-editbox-0").html(); //-ftp : Make stub editable in RTE let stubText = $('#stub').val() n.s = stubText console.log(stubText) n.creator = localStorage.user //- ftp : save body tow web3 mindgraph.dot.update(n); // Q&D generate slides localStorage.slides = m.u.doSlides(n) //- Q&D : svae curent node anchor when generating slides // so that we can get the image for the dot when processing /reveal?/slides localStorage.CN = n.a // hide nested lists so that only top level items can be seen // $('ul>ul').hide() /* if (oldTrailMarks && parent.opidox.rte4mg) { parent.opidox.rte4mg.action.updateHTML4DOT(n) }*/ //- issue : on page save in editor replicate across devices async function replicateViaOpenChannel(n) { // window.hostapp.run('/replicate', n) // dim. // window.hostapp.run('#replicate',n) async function createPushEvent(n) { let origDot = JSON.parse(JSON.stringify(n)) let message //- step : append first character in peerName to anchor for node function getPeerName() { return localStorage.peerName } function compactPageBodyHtml(body) { return body.replace(/ /g, ' ') } n.b = compactPageBodyHtml(n.b) let encoded = encodeURIComponent(n.b); //TODO consider converting to markdown for transfer if (encoded.length > 10000) { alert("encoded body larger than limit" + encoded.length) // return } else { alert("encoded length " + encoded.length) } //- QD set shared to true on dot n.shared = moment().format("YYYY MM DD hh:mm") const peerName = getPeerName() const peerEmoji = peerName.substring(0, 2) n.a = n.a + peerEmoji n.t = n.t + ' ' + peerEmoji const from = localStorage.peerName const event = 'push' const action = 'save' const to = "all" //- ftp : warning on large body console.log(n.b.length) let sizeLimit = 10000 let provenance = $('#provenance').is(':checked') debugger if (provenance || encoded.length > sizeLimit) { //- feat : save body to web3storage //- ftp : dynamically loading Web3Frame debugger let web3StorageIframe = document.getElementById("Web3Frame") debugger web3StorageIframe.src = location.pathname + '../web3/' + '?' + moment().unix() //- ftp : increase height of web3StorageIframe before loading web3StorageIframe.height = "520px" debugger web3StorageIframe.addEventListener('load', async function () { let bodyLink = await web3storage.store.body(n, window.wweb3p) //- feat : unshift bodyLinks to dot.bsl array n.blsa = n.blsa || [] n.blsa.unshift(bodyLink) origDot.blsa = n.blsa window.mindgraph.dot.update(n) window.mindgraph.dot.update(origDot) n.b = `<a target="_blank" href="https://${bodyLink}">body</a>` const data = [n] message = JSON.stringify({ event, action, from, to, data }) //hub.scld.x.add(message) //- feat : invoke webnative intent hub chat as fetch get request debugger open(location.pathname + '../hub/chat/?' + encodeURIComponent(JSON.stringify(message))) }) } else { if (true) { // fall through to create push event const data = [n] message = JSON.stringify({ event, action, from, to, data }) //hub.scld.x.add(message) debugger let href = location.origin + location.pathname + '../hub/chat/?' open(href + encodeURIComponent(message), "chat") //- fix : SclaDrone size limit window.hub.odb.setupOwnHub('', async function (db) { const hash = await db.add([n]) //- ftp : replace body of dot with hash in message sent via cladrone n.b = "- hash : " + hash //- ftp : set hs property to an array of hashes in reverse chronological order n.hs = n.hs || [] n.hs.push(hash) const data = [n] const message = JSON.stringify({ event, action, from, to, data, hash }) $('#log').append(message) // replaced by open window // hub.scld.x.add(message) open(location.pathname + '../hub/chat/?' + encodeURIComponent(JSON.stringify(message))) }) } } } debugger createPushEvent(n) } //Q&D shared property is set so share again let share = $('#shareCheckBox').is(':checked') || n.shared; if (share) { // allways replicate via open channel replicateViaOpenChannel(n) return //- ftp : load IndyHubIframe on save let indieHubFrame = document.getElementById("IndieHubFrame") if (!indieHubFrame.src) { indieHubFrame.src = host + "/hub" + location.search + "&ts=" + moment().unix() indieHubFrame.contentWindow.addEventListener('load', function () { }, false) } else { replicateViaOpenChannel(n) } } //- ftp : trigger /minddrive/save in opener with changes from editor // TODO /** * - ftp : dead wood hostapp if (window.hostapp) { let ps = new URLSearchParams(location.search.substring(1)) let path = ps.get('p') //- issue : add share checkbox to editor window.hostapp.run('/minddrive/save', { path: path, share: share }, n) //- ftp : ensure new text is setup for editing opidox.rte.setup() //- issue : show if saved and prevent reload if edited but not saved $('#isSaved').prop('checked', true) } else { alert('hostapp not set') } */ } _rte.createLink = function (text) { if (text.startsWith("http")) { return text; } else { } } /** * * @param {*} prop propery name * @returns * - feat : opidox.rte.interpretPropOnPage */ _rte.getPropFromPage = function (prop) { let value let text = $('.rte-editbox').html() let index = text.indexOf('- ' + prop + ' :') //- fix : allow string values in property setting if (index === -1) { return } text = text.substring(index) let indexEnd = text.indexOf('</div>') text = '<div>' + text.substring(0, indexEnd) + '</div>' value = $(text).children() if (value.length === 0) { let bit = $(text).text() value = bit.substring(prop.length + 5) } let binding = {} binding[prop] = value return binding } _rte.cleanElem = function (sel) { $(sel).toArray().forEach(function (elem, i) { //- fix : do not remove attributes for elems with ids while (elem.attributes.length > 0 && !elem.id) elem.removeAttribute(elem.attributes[0].name); }) } _rte.removeNamedAttribute = function (sel, attr) { $(sel).toArray().forEach(function (elem, i) { elem.removeAttribute(attr); }) } _rte.removeSpans = function () { } _rte.interpretPropOnPage = function (prop) { let propertyBinding = _rte.getPropFromPage(prop) if (!propertyBinding[prop]) { alert("no prop " + prop) return } //- fix : interpreter not defined let interpreter = { i: function (val) { //- fn : setHeightOnImage function setHeightOnImage() { $('img', '.rte-editbox').height("32") } setHeightOnImage() let imageSrc = val[0]['src'] // console.log(n) if (n.i !== imageSrc) { n.i = imageSrc _rte.save(n) } } } let f = interpreter[prop] let a = propertyBinding[prop] f(a) } _rte.getSelectionText = function () { var text = ""; var activeEl = document.activeElement; var activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null; if ( (activeElTagName == "textarea") || (activeElTagName == "input" && /^(?:text|search|password|tel|url)$/i.test(activeEl.type)) && (typeof activeEl.selectionStart == "number") ) { text = activeEl.value.slice(activeEl.selectionStart, activeEl.selectionEnd); } else if (window.getSelection) { text = window.getSelection().toString(); } return text; } return _rte; })() window.opidox.rte = opidox.rte // introduce rte.munge.munge = (function () { opidox.rte.munge = (function () { let _munge = {} _munge.section = function () { let sections = $('section') debugger let backgroundImageUrl = $(sections[1]).css('background-image') return backgroundImageUrl.substring(5,backgroundImageUrl.length-2) } return _munge })() opidox.rte.annote = (function () { let pageUrl = "http://hsc.fed.wiki/view/hypertext-super-collaborator" let _annote = {} _annote.t = function () { let frame = `<iframe id="loader"/>` } return _annote })() opidox.rte.action = (function () { let _action = {} _action.autoSave = function () { } _action.createDot = function (title, stub) { // let mg3 = mg2 || mg3 // let ns = parent.mg3.dot.byTitle(title, true); //- ftp : restore opidox.rte.createDot let ns = mindgraph.dot.byTitle(title, true) if (!stub) { stub = title } let n; if (ns.length === 0) { n = { t: title, s: stub, i: 'https://bafybeihvcrddb4zbq5t4zuj7q4esabf5mizoxvkjvoac7gmmf73fcnqou4.ipfs.w3s.link/dot.png' } n = mindgraph.dot.createFromJSON(n) } else { n = ns[0] n.stub = stub } mindgraph.dot.save(n) return n } return _action })() /** * TrailMarker capabilities */ window.trailmarker = function () { function doMark() { // alert("not in use") //https://stackoverflow.com/questions/1197401/get-element-node-at-caret-position-in-contenteditable let currentNode = document.getSelection().anchorNode.parentNode let currentHTML = currentNode.innerHTML + ' ' // let inputBox = '<input type="text"></input>' let bit = currentNode.tagName === "LI" ? currentNode.innerHTML = currentHTML + inputBox + '<ul><li></li><li></li></ul>' : currentHTML let newBit = $('<div>' + currentHTML + inputBox + '<ul><li></li><li></li></ul></div>') if (currentNode.tagName !== 'LI') { x = $(currentNode).after(newBit) $(currentNode).remove() $('input', newBit).focus() } else { $('input', newBit).focus() } $('input').blur(function (ev) { $(ev.target).replaceWith(ev.target.value) }) } function doSection() { alert("Do Section") doMark() } function doHash() { alert('#') } function setupKeyHandlers() { //- Q&D : block keyhandlers in editor // https://medium.com/@albertogasparin/getting-plain-text-from-user-input-on-a-contenteditable-element-b711aba2cb36 // $('div').keydown(onKeyDown) // $('li').keydown(onKeyDown) //- issue : show if saved and prevent reload if edited but not saved // unchecked isSaved chackbox on input $('.rte-editbox').on('input', function (ev) { $('#isSaved').prop('checked', '') }) } var _trm = {} _trm.setup = setupKeyHandlers let last = '' function onKeyDown() { let keyMap = { 187: '=', 189: '-', 32: ' ', 71: 'g', 72: 'h', 77: 'm', 222: '#' } let actionMap = { "- ": doMark, "= ": doSection, " #": doHash } var key = window.event.keyCode; console.log(key) let handlerKey = keyMap[key] console.log(handlerKey) if (last) { let action = actionMap[last + handlerKey] if (action) { (action)() last = '' } } if (handlerKey) { last = handlerKey } return true; } return _trm }() /** * https://codepen.io/davidkacha/pen/zzNBxq * * @param html */ function formatFactory(html) { if (html === "undefined") { return "<div> </div?" } function parse(html, tab = 0) { var tab; var html = $.parseHTML(html); var formatHtml = new String(); function setTabs() { var tabs = new String(); for (var i = 0; i < tab; i++) { tabs += ' '; } return tabs; }; $.each(html, function (i, el) { if (el.nodeName == '#text') { if (($(el).text().trim()).length) { formatHtml += setTabs() + $(el).text().trim() + '\n'; } } else { var innerHTML = $(el).html().trim(); $(el).html(innerHTML.replace('\n', '').replace(/ +(?= )/g, '')); if ($(el).children().length) { $(el).html('\n' + parse(innerHTML, (tab + 1)) + setTabs()); var outerHTML = $(el).prop('outerHTML').trim(); formatHtml += setTabs() + outerHTML + '\n'; } else { var outerHTML = $(el).prop('outerHTML').trim(); formatHtml += setTabs() + outerHTML + '\n'; } } }); return formatHtml; }; return parse(html.replace(/(\r\n|\n|\r)/gm, " ").replace(/ +(?= )/g, '')); }; //- ftp : define function getFromHive async function getFromHive(a) { let r = await fetch('https://localhost:448/dots/?as=' + a) let text = await (r).text() return JSON.parse(text)[0].b } window.gh = getFromHive //- ftp : breakText function breakText(element) { $(element).each(function (i, x) { let bit = x.innerText bit = bit.replace(/,/g, ",</div>\n<div>") x.innerText = bit }) }